#include <iostream>
#include <queue>
#include <pthread.h>
#include <unistd.h>
#include "lockGuard.hpp"
#define BLOCK_NUM 5

template<class T>
class BlockQueue
{
private:
    bool Empty()
    {
        return bq_.size() == 0;
    }
    bool Full()
    {
        return bq_.size() == capacity_;
    }
public:
    BlockQueue(int capacity = BLOCK_NUM)
        :capacity_(capacity)
    {
        pthread_mutex_init(&mtx_,nullptr);
        pthread_cond_init(&empty_,nullptr);
        pthread_cond_init(&full_,nullptr);
    }
    void push(const T& in)
    {
        // pthread_mutex_lock(&mtx_);
        // // 1.先检测当前的临界区是否能够满足访问条件
        // // pthread_cond_wait：我是在临界区中，我是持有锁的，如果我去等待了，锁该怎么办
        // // pthread_cond_wait：第二个参数是一个锁，当成功调用wait后，该锁会被自动释放
        // // 当我被唤醒的时候，我是从哪里醒来的？从哪里阻塞挂起，就从哪里醒来，我们是在临界区被唤醒的
        // // 当我们被唤醒的时候，pthread_cond_wait会自动帮我们申请锁
        // // pthread_cond_wait: 但是只要是一个函数，就可能调用失败
        // // pthread_cond_wait: 可能存在 伪唤醒 的情况
        // while(Full()) pthread_cond_wait(&full_,&mtx_);
        // // 2.访问临界资源，100%确定，资源是就绪的
        // bq_.push(in);
        // //if(bq_.size() >= capacity_/2) pthread_cond_signal(&empty_);
        // pthread_cond_signal(&empty_);
        // pthread_mutex_unlock(&mtx_);
        lockGuard lockguard(&mtx_);
        while(Full()) pthread_cond_wait(&full_,&mtx_);
        
        bq_.push(in);
        pthread_cond_signal(&empty_);
    }
    void pop(T* out)
    {
        // pthread_mutex_lock(&mtx_);
        // while(Empty()) pthread_cond_wait(&empty_,&mtx_);
        // *out = bq_.front();
        // bq_.pop();
        // pthread_cond_signal(&full_);
        // pthread_mutex_unlock(&mtx_);
        lockGuard lockguard(&mtx_);
        while(Empty()) pthread_cond_wait(&empty_,&mtx_);
        *out = bq_.front();
        bq_.pop();
        pthread_cond_signal(&full_);
    }

private:
    std::queue<T> bq_;     // 阻塞队列
    int capacity_;         // 容量上限
    pthread_mutex_t mtx_;  // 通过互斥锁来保证线程安全
    pthread_cond_t empty_; // 表示queue是否为空
    pthread_cond_t full_;  // 表示queue是否为满
};